Transfer authorization to manager signature authentication, reducing the possibility of key authentication leakage#259
Transfer authorization to manager signature authentication, reducing the possibility of key authentication leakage#259Admirepowered wants to merge 11 commits intobmax121:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR shifts privileged command authorization from a “superkey” model toward an Android “trusted manager” model, where a manager app is identified via APK signature digest + package UID, and then used to gate su/supercmd/supercall behaviors.
Changes:
- Add trusted-manager discovery/refresh logic (APK path discovery + signature/cert digest check + packages.list UID lookup) and use it to authorize privileged flows on Android.
- Update
supercmd/supercallauthorization to allow trusted-manager UID in addition to existing allowlists, plus add boot-event refresh hooks. - Add
iterate_dirksym plumbing and a PI_MAP patch condition fix.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/patch.c | Fixes PI_MAP patch condition handling for newer kernel versions. |
| kernel/patch/ksyms/misc.c | Adds/matches iterate_dir and enables matching call_usermodehelper. |
| kernel/patch/include/userd.h | Adds Android trusted-manager API declarations and includes ktypes.h. |
| kernel/patch/common/user_event.c | Refreshes trusted-manager state on boot events and logs results. |
| kernel/patch/common/supercmd.c | Extends authorization to trusted-manager UID; adds reload-cfg logging. |
| kernel/patch/common/supercall.c | Extends authorization to trusted-manager UID; adds trusted-manager wrapper. |
| kernel/patch/android/userd.c | Implements trusted-manager identification (APK scan + signature digest) and auto-su on exec for trusted-manager. |
| kernel/linux/include/linux/fs.h | Introduces dir_context + iterate_dir() wrapper for ksym-based directory scanning. |
| kernel/.gitignore | Expands ignored build/artifact patterns. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| uint8_t cert_buf[APK_CERT_MAX_LENGTH]; | ||
|
|
There was a problem hiding this comment.
apk_sig_block_matches_trusted_digest() allocates a 4096-byte cert_buf on the stack. Kernel stack space is tight, and this function is called from deeper call chains during boot/event handling; this size risks stack overflow on some configs. Prefer allocating the certificate buffer dynamically (e.g., kmalloc/kvzalloc) or reusing a shared buffer, and ensure it’s freed on all paths.
| static int path_has_suffix(const char *path, const char *suffix) | ||
| { | ||
| loff_t off = 0; | ||
| set_priv_sel_allow(current, true); | ||
| size_t path_len; | ||
| size_t suffix_len; | ||
| if (!path || !suffix) return 0; | ||
| path_len = strlen(path); | ||
| suffix_len = strlen(suffix); | ||
| if (path_len < suffix_len) return 0; | ||
| return strcmp(path + path_len - suffix_len, suffix) == 0; | ||
| } | ||
|
|
||
| static int path_is_exact(const char *path, const char *target) | ||
| { | ||
| return path && target && strcmp(path, target) == 0; | ||
| } |
There was a problem hiding this comment.
path_has_suffix() and path_is_exact() are newly added but currently unused in this file, which will trigger -Wunused-function warnings under -Wall and adds dead code to maintain. Either remove them or use them where appropriate.
| int refresh_trusted_manager_state(void); | ||
| int is_trusted_manager_uid(uid_t uid); | ||
| uid_t get_trusted_manager_uid(void); | ||
|
|
There was a problem hiding this comment.
userd.h declares is_trusted_manager_uid() only under #ifdef ANDROID, but common code paths (e.g., patch/common/supercmd.c) now call it outside #ifdef ANDROID. To avoid non-Android build breakage and keep the API consistent, consider declaring is_trusted_manager_uid() unconditionally in the header (with a safe non-Android stub/implementation that denies by default).
| #else | |
| static inline int is_trusted_manager_uid(uid_t uid) | |
| { | |
| (void)uid; | |
| return 0; | |
| } |
| is_key_auth = 1; | ||
| } else if (!strcmp("su", arg1)) { | ||
| uid_t uid = current_uid(); | ||
| if (!is_su_allow_uid(uid)) return; | ||
| if (!is_su_allow_uid(uid) && !is_trusted_manager_uid(uid)) return; | ||
| su_allow_uid_profile(0, uid, &profile); | ||
| } else { | ||
| return; | ||
| if (!is_trusted_manager_uid(current_uid())) return; | ||
| } |
There was a problem hiding this comment.
handle_supercmd() now calls is_trusted_manager_uid() in the non-#ifdef ANDROID control flow, but this file only includes <userd.h> when ANDROID is defined. In non-Android builds (supported by the Makefile), this will be a hard compile error (no prototype) and also changes authorization behavior unexpectedly. Consider either (a) wrapping the trusted-manager checks in #ifdef ANDROID and preserving the previous non-Android behavior, or (b) including <userd.h> unconditionally and providing a safe non-Android implementation of is_trusted_manager_uid() that denies by default.
| #ifdef ANDROID | ||
| return is_trusted_manager_uid_android(uid); | ||
| #endif | ||
| return 1; |
There was a problem hiding this comment.
In non-Android builds, is_trusted_manager_uid() currently returns 1, which effectively treats every UID as a trusted manager. That makes the new authorization checks no-ops and can unintentionally widen access (e.g., allowing su for any UID). Returning 0 (deny) when ANDROID is not defined would preserve the pre-existing non-Android security posture.
| return 1; | |
| return 0; |
| int trust_rc = refresh_trusted_manager_state(); | ||
| int config_rc = load_ap_package_config(); | ||
| log_boot("reload-cfg: refresh rc=%d package_config rc=%d\n", trust_rc, config_rc); | ||
| cmd_res.msg = "reload package config success"; |
There was a problem hiding this comment.
The reload-cfg command logs refresh_trusted_manager_state() / load_ap_package_config() return codes but always reports success to the caller. If either call fails, the command output should reflect that (e.g., set cmd_res.rc / cmd_res.err_msg or choose a failure message) so users don’t get a false positive.
| int trust_rc = refresh_trusted_manager_state(); | |
| int config_rc = load_ap_package_config(); | |
| log_boot("reload-cfg: refresh rc=%d package_config rc=%d\n", trust_rc, config_rc); | |
| cmd_res.msg = "reload package config success"; | |
| static char reload_cfg_err[128]; | |
| int trust_rc = refresh_trusted_manager_state(); | |
| int config_rc = load_ap_package_config(); | |
| log_boot("reload-cfg: refresh rc=%d package_config rc=%d\n", trust_rc, config_rc); | |
| if (trust_rc || config_rc) { | |
| cmd_res.rc = trust_rc ? trust_rc : config_rc; | |
| snprintf(reload_cfg_err, sizeof(reload_cfg_err), | |
| "reload package config failed: refresh rc=%d package_config rc=%d", | |
| trust_rc, config_rc); | |
| cmd_res.err_msg = reload_cfg_err; | |
| } else { | |
| cmd_res.msg = "reload package config success"; | |
| } |
| *len = vfs_llseek(filp, 0, SEEK_END); | ||
| vfs_llseek(filp, 0, SEEK_SET); | ||
| data = vmalloc(*len); | ||
| data = vmalloc(*len + 1); | ||
| if (!data) { | ||
| filp_close(filp, 0); | ||
| goto out; | ||
| } | ||
| loff_t pos = 0; | ||
| kernel_read(filp, data, *len, &pos); | ||
| ((char *)data)[*len] = '\0'; |
There was a problem hiding this comment.
kernel_read_file() uses vfs_llseek() to populate *len and then immediately does vmalloc(*len + 1) and reads *len bytes. If vfs_llseek() fails and returns a negative value, this will turn into a huge allocation attempt (and *len + 1 can also overflow). Add validation that *len is >= 0 and within a reasonable upper bound before allocating/reading.
No description provided.